use rquickjs::{module::ModuleDef, Ctx, Function};

pub struct RKCalc{
    a0: f32,
    b0: f32,
    eps: f32,
}

impl RKCalc {
    const R: f32 = 8.314;
    const MAX_LOOPS: i32 = 1000;
    pub fn new(tc:f32, pc:f32, eps: f32) -> Self{
        let a0 = 0.4278 * Self::R.powi(2) * tc.powf(2.5) / pc;
        let b0 = 0.08664 * Self::R * tc / pc;
        Self { a0, b0, eps}
    }

    pub fn mole(&self, n0: f32, p: f32, v: f32, t: f32) -> Option<f32> {
        self.newton(n0, p, v, t)
    }

    fn newton(&self, n0: f32, p: f32, v: f32, t: f32) -> Option<f32> {
        let mut loops = 1;
        let mut n0 = n0;
        while loops <= Self::MAX_LOOPS {
            let n = n0 - self.func(n0, p, v, t) / self.func_d(n0, p, v, t, self.eps);
            let  res = n - n0;
            println!("loop: {}, residual errors: {}", loops, res);
            if res.abs() < self.eps{
                return Some(n);
            }
            n0 = n;
            loops += 1;
        }
        None
    }

    fn func(&self, n: f32, p: f32, v: f32, t: f32) -> f32 {
        return (p + self.a0/v*n/(v/n + self.b0)*t.powf(-0.5))*(v/n - self.b0) - Self::R*t;
    }

    fn func_d(&self, n: f32, p: f32, v: f32, t: f32, eps: f32) -> f32 {
        return (self.func(n + eps, p, v, t) - self.func(n, p, v, t)) / eps;
    }
}

pub struct CalcModule;

impl CalcModule{
    pub fn calc(tc:f32, pc:f32, eps:f32, n0:f32, p: f32, v: f32, t: f32) -> Option<f32>{
        let rk_calc = RKCalc::new(tc, pc, eps);
        rk_calc.mole(n0, p, v, t)
    }
}

impl ModuleDef for CalcModule {
    fn declare<'js>(decl: &rquickjs::module::Declarations<'js>) -> rquickjs::Result<()> {
        decl.declare("r")?;
        decl.declare("mole")?;
        Ok(())
    }

    fn evaluate<'js>(ctx: &Ctx<'js>, exports: &rquickjs::module::Exports<'js>) -> rquickjs::Result<()> {
        exports.export("r", RKCalc::R)?;
        exports.export("mole", Function::new(ctx.clone(), |tc, pc, eps, n0, p, v, t| CalcModule::calc(tc, pc, eps, n0, p, v, t))?.with_name("mole"))?;
        Ok(())
    }
}

